home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / AfterDark / After Dark 2.0 Modules / PeekaBoo / Peek-a-Boo.c < prev    next >
Text File  |  1993-09-12  |  17KB  |  539 lines

  1.  
  2.  
  3. #include <QuickDraw.h>
  4. #include <Memory.h>
  5. #include <Resources.h>
  6.  
  7. #include <QDoffscreen.h>
  8.  
  9. #include "GraphicsModule_Types.h"
  10. #include "Sounds.h"
  11.  
  12. unsigned short RangedRdm( unsigned short min, unsigned short max );
  13.  
  14. // these are the functs that need defined ...
  15. OSErr DoInitialize(Handle *storage, RgnHandle blankRgn, GMParamBlockPtr params);
  16. OSErr DoClose(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  17. OSErr DoBlank(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  18. OSErr DoDrawFrame(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params);
  19. OSErr DoSetUp(RgnHandle blankRgn, short message, GMParamBlockPtr params);
  20.  
  21. // extra ones
  22. OSErr DoSelected(RgnHandle blankRgn, short message, GMParamBlockPtr params);
  23. OSErr DoAboutBox(RgnHandle blankRgn, short message, GMParamBlockPtr params);
  24.  
  25.  
  26. #define ONE_KNOCK_SND    256
  27. #define BASE_PICTID        127            // this is 1 less than the starting ID number (128)
  28.  
  29. #define NUMPICTS        26    
  30.  
  31. // states for the simple state machine ...
  32. #define STATE_DOWN            0
  33. #define STATE_GOING_UP        1
  34. #define STATE_UP            2
  35. #define STATE_LOOKING        3
  36. #define STATE_GOING_DOWN    4
  37.  
  38.  
  39. // this is borrowed from the example code from Bouncing Ball ...
  40. /* some macros to simplify synchronizing to the vertical retrace. */
  41. #define SynchFlag(m) (params->monitors->monitorList[m].synchFlag)
  42. #define SynchVBL(m) synchFlag = &SynchFlag(m); *synchFlag = false; while(!*synchFlag);
  43.  
  44.  
  45.  
  46. typedef struct infostruct {
  47.     Boolean        soundAvailable;            // do we have sound?
  48.     Handle        oneKnockSound;            // one knock sound
  49.     GWorldPtr    gMyOffG;                // offscreen graphics world
  50.  
  51. // the face stuff
  52.     PicHandle    thePict;                // the face to slide up and down
  53.     PicHandle    eyes, eyesleft, eyesright;    // the eyes to slide left/right
  54.     Point        eyesPt;                    // where they eyes are supposed to be drawn
  55.     Handle        faceSound;                // a sound associated with a face
  56.     short        thePictNumber;            // the number of the picture we're using
  57.     Rect        theRect;                // rects for the sliding face
  58.     short        thePictWidth;            // time saving info on rect's width
  59.     short        thePictHeight;            // "    "        "        "       height
  60.  
  61.     SoundInfoHandle    soundInfo;            // AD sound info handle
  62.     Rect        theScreen;                // the monitor's screen rect
  63.     long        nextSoundTick;            // last tick when a the knocking sound was played
  64.     short        state;                    // the state we're in
  65. } infostruct, *infostructPtr, **infostructHandle;
  66.  
  67.  
  68.  
  69. // some prototypes ...
  70. void do_some_knocking(infostructPtr info, GMParamBlockPtr params);
  71. void slide_picture_up(infostructPtr info, GMParamBlockPtr params);
  72. void slide_picture_down(infostructPtr info, RgnHandle blankRgn, GMParamBlockPtr params);
  73. void slide_eyes_around(infostructPtr info, GMParamBlockPtr params);
  74.  
  75. //////////////////////////////////////////////////////////////////////////////////////
  76. // this is the first funct called by AD ... we need to allocate and initialize here
  77. OSErr
  78. DoInitialize(Handle *storage, RgnHandle blankRgn, GMParamBlockPtr params) {
  79.  
  80. // ERROR MESSAGES ....
  81.     StringPtr         memoryMessage = (StringPtr)"\pmodule: Not enough memory!";
  82.     StringPtr         loadMessage = (StringPtr)"\pmodule: Could not load pict!";
  83.     StringPtr        offscreenMessage = (StringPtr)"\pmodule: Offscreen Bitmap failed!";
  84.     StringPtr        colorMessage = (StringPtr)"\pmodule: Must have color!";
  85.     StringPtr        menuMessage = (StringPtr)"\pmodule: Failed to get MENU!!";
  86. // the variables ...
  87.     Handle             h;
  88.     Handle            tempHandle;
  89.     infostructPtr    mystorage;
  90.     GWorldPtr        currPort;        // something to save the port + device
  91.     GDHandle        currDev;
  92.     PixMapHandle    pixBase;
  93.     
  94.     // lets ensure we're running on a screen of some depth ... (yes? no? do we care?)
  95.     // no ... I don't care ...just go on and run and let it look wierd
  96.     // but we do need some color Quickdraw - I haven't put in the B&W offscreen 
  97.     // routines to do offscreen bitmaps 
  98.     if (!params->colorQDAvail) {
  99.         BlockMove(colorMessage, params->errorMessage, 1 + colorMessage[0]);
  100.         return ModuleError;                    // could not allocate space
  101.     }
  102.     
  103.     // allocate space here for my structure - whatever it may be
  104.     h = NewHandle( sizeof(infostruct) );    
  105.     
  106.     if (h == (Handle)nil) {
  107.         *storage = nil;
  108.         h = nil;
  109.         BlockMove(memoryMessage, params->errorMessage, 1 + memoryMessage[0]);
  110.         return ModuleError;                    // could not allocate space
  111.     }
  112.     
  113.     // Randomize...
  114.     params->qdGlobalsCopy->qdRandSeed = TickCount();
  115.     
  116.     // handle pointer to have
  117.     *storage = h;
  118.     
  119.     // lock down our storage so we can refer to it by pointer safely
  120.     MoveHHi(h);
  121.     HLock(h);    
  122.     
  123.     mystorage = (infostructPtr) *h;
  124.  
  125. ///////// load the sounds ...
  126.     // do we have sound support?
  127.     mystorage->soundAvailable = (params->systemConfig & SoundAvailable) != 0;
  128.  
  129.     if (mystorage->soundAvailable) {
  130.     
  131.         /* load the resources for our  sounds. */
  132.         mystorage->oneKnockSound = GetResource('snd ', ONE_KNOCK_SND);
  133.     
  134.         /* to use the sound functions in AD 2.0u we must pass in "params" */
  135.         mystorage->soundInfo = OpenSound(params);
  136.     }
  137.  
  138.         
  139.     mystorage->thePictNumber = params->controlValues[2]; 
  140.     mystorage->thePict = 
  141.             (PicHandle)GetResource('PICT', BASE_PICTID + mystorage->thePictNumber);
  142.  
  143.     if (mystorage->thePict == (PicHandle)nil) {
  144.         DoClose( h, (RgnHandle) nil, (GMParamBlockPtr) nil);
  145.         BlockMove(loadMessage, params->errorMessage, 1 + loadMessage[0]);
  146.         return ModuleError;                        // could not load pict
  147.     }
  148.         
  149.  
  150. ////// get it's sound ... if it's possible
  151.         mystorage->faceSound = GetResource('snd ', BASE_PICTID + mystorage->thePictNumber);
  152.  
  153. ////// get the eye pictures ... if it's possible
  154.     mystorage->eyes = (PicHandle)GetResource('PICT', 
  155.             ((BASE_PICTID + mystorage->thePictNumber) * 10) + 1);
  156.     mystorage->eyesleft = (PicHandle)GetResource('PICT', 
  157.             ((BASE_PICTID + mystorage->thePictNumber) * 10) + 2);
  158.     mystorage->eyesright = (PicHandle)GetResource('PICT', 
  159.             ((BASE_PICTID + mystorage->thePictNumber) * 10) + 3);
  160.  
  161. ////// get the eye Point resource ... if it's possible
  162.     if ( (tempHandle = GetResource('eyes', (BASE_PICTID + mystorage->thePictNumber))) != 
  163.             (Handle)nil) {
  164.         PointPtr p;
  165.         
  166.         p = (PointPtr)(*tempHandle);
  167.         mystorage->eyesPt = (*p);
  168.         ReleaseResource( tempHandle);
  169.     } else 
  170.         mystorage->eyesPt.h = mystorage->eyesPt.v = -1;
  171.     
  172. ///////
  173.     mystorage->theRect =  (*mystorage->thePict)->picFrame;
  174.     mystorage->thePictWidth = mystorage->theRect.right - mystorage->theRect.left;
  175.     mystorage->thePictHeight = mystorage->theRect.bottom - mystorage->theRect.top;
  176.     
  177.     
  178. //////////////////////////////////////////////////////////////////
  179. // lets setup the offscreen world
  180.     // save the current info
  181.     GetGWorld(&currPort,&currDev);
  182.     
  183.     // create the offscreen world (with the bounds of the picture)
  184.     if (NewGWorld(&(mystorage->gMyOffG), 0, &mystorage->theRect, nil, nil, 0) != noErr) {
  185.         DoClose( h, (RgnHandle) nil, (GMParamBlockPtr) nil);
  186.         BlockMove(offscreenMessage, params->errorMessage, 1 + offscreenMessage[0]);
  187.         return ModuleError;            
  188.     }
  189.     // keep it from moving
  190.     pixBase = GetGWorldPixMap( mystorage->gMyOffG );
  191.     LockPixels (pixBase);
  192.     
  193.     // point to the offscreen world
  194.     SetGWorld ((mystorage->gMyOffG), nil);
  195.     
  196.     // draw it
  197.     DrawPicture( mystorage->thePict, &mystorage->theRect );
  198.     // could release the resource now, you know.
  199.  
  200.     // done drawing, set the world back 
  201.     SetGWorld (currPort, currDev);
  202.     
  203.     // unlock those puppies
  204.     UnlockPixels (pixBase);
  205. //////////////////////////////////////////////////////////////////
  206.  
  207.     
  208.     mystorage->theScreen = params->monitors->monitorList[0].bounds;
  209.     mystorage->nextSoundTick = 0;
  210.     mystorage->state = STATE_DOWN;
  211.     HUnlock(h);
  212.     
  213.     return noErr;
  214. }
  215.  
  216. //////////////////////////////////////////////////////////////////////////////////////
  217. // the screen saver has been awakened! time to ditch the storage and wave goodbye
  218. OSErr 
  219. DoClose(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params) {
  220.     
  221.     infostructPtr    mystorage;
  222.     
  223.     HLock(storage);
  224.     
  225.     mystorage = (infostructPtr)*storage;
  226.     
  227.     if (mystorage->soundAvailable) {
  228.         CloseSound( mystorage->soundInfo, params->sndChannel);
  229.         
  230.         // knocking sound
  231.         if (mystorage->oneKnockSound != (Handle)nil)
  232.             ReleaseResource(mystorage->oneKnockSound);
  233.  
  234.         // the face's sound
  235.         if (mystorage->faceSound != (Handle)nil)
  236.             ReleaseResource(mystorage->faceSound);
  237.  
  238.     }
  239.     
  240.     
  241.     ReleaseResource( (Handle)mystorage->thePict);
  242.     // release the eye picts ... if they're there
  243.     if (mystorage->eyes != (PicHandle)nil)
  244.         ReleaseResource( (Handle)mystorage->eyes);
  245.     if (mystorage->eyesleft != (PicHandle)nil)
  246.         ReleaseResource( (Handle)mystorage->eyesleft);
  247.     if (mystorage->eyesright != (PicHandle)nil)
  248.         ReleaseResource( (Handle)mystorage->eyesright);
  249.         
  250.         
  251.     DisposeGWorld( mystorage->gMyOffG);
  252.     HUnlock(storage);
  253.     DisposHandle( storage);
  254.     return noErr;
  255. }
  256.  
  257.  
  258.  
  259. //////////////////////////////////////////////////////////////////////////////////////
  260. // make the screen go black
  261. OSErr
  262. DoBlank(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params) {
  263.  
  264.     FillRgn(blankRgn, params->qdGlobalsCopy->qdBlack);
  265.     return noErr;
  266.  
  267. }
  268.  
  269. //////////////////////////////////////////////////////////////////////////////////////
  270. // this is the workhorse routine. It does the continual screen work to make
  271. // this screen saver what it is.
  272. OSErr 
  273. DoDrawFrame(Handle storage, RgnHandle blankRgn, GMParamBlockPtr params) {
  274.     infostructPtr    info;
  275.  
  276.     HLock(storage);
  277.     info = (infostructPtr)*storage;
  278.  
  279.     if ( (params->controlValues[2]) != info->thePictNumber) {
  280.         DoClose(storage, blankRgn, params);    // ditch the storage
  281.         return RestartMe;                    // tell AD to reinit us
  282.     }
  283.  
  284.  
  285.     // when sufficient time has passed ... the face should peep up
  286.     
  287.     switch (info->state) {
  288.     case STATE_DOWN:
  289.             do_some_knocking(info, params);
  290.             break;
  291.     case STATE_GOING_UP:
  292.             slide_picture_up(info, params);        
  293.             break;
  294.     case STATE_UP:
  295.             info->state = STATE_LOOKING;
  296.             break;
  297.     case STATE_LOOKING:
  298.             slide_eyes_around(info, params);
  299.             break;        
  300.     case STATE_GOING_DOWN:
  301.             slide_picture_down(info, blankRgn,  params);
  302.             break;
  303.     default:
  304.         info->state = STATE_DOWN; // this should not happen
  305.         break;
  306.     }
  307.     HUnlock(storage);
  308.     return noErr;
  309. }
  310. //////////////////////////////////////////////////////////////////////////////////////
  311. // this is called when they click on something in the control panel
  312. OSErr 
  313. DoSetUp(RgnHandle blankRgn, short message, GMParamBlockPtr params) {
  314.  
  315.     return noErr;
  316. }
  317.  
  318.  
  319. void
  320. slide_eyes_around(infostructPtr info, GMParamBlockPtr params)
  321. {
  322.  
  323.     // if there are eyes, eyesleft, eyesright, an eyes point *AND* a random chance
  324.     // then go ahead and play with the eyes
  325.     if (info->eyes != (PicHandle)nil &&
  326.             info->eyesleft != (PicHandle)nil &&
  327.                 info->eyesright != (PicHandle)nil &&
  328.                     info->eyesPt.h != -1 &&
  329.                     info->eyesPt.v != -1 &&
  330.                     (!(Random()%10)) ) {
  331.         Rect r;
  332.         short tempnum;
  333.         
  334.         r = (*info->eyes)->picFrame;
  335.         // what if the picFrame is not 0,0 origined? 
  336.  
  337.         // offset to where the pixmap's rect actually is on screen
  338.         OffsetRect( &r, info->theRect.left, info->theRect.top);
  339.         
  340.         // offset to where the eyes resource says it is
  341.         OffsetRect( &r, info->eyesPt.h, info->eyesPt.v); 
  342.         
  343.         // randomly choose which eye pict to display
  344.         tempnum = RangedRdm(0, 5);
  345.         switch(tempnum) {
  346.             case 0:  DrawPicture( info->eyesleft, &r); break;
  347.             case 1:  DrawPicture( info->eyesright, &r); break;
  348.             // case 2, 3, 4, 5 ... all do normal eye things in the default
  349.             default: DrawPicture( info->eyes, &r); break;
  350.         }
  351.     }
  352.     
  353.     // make some noise, also?
  354.     if (!SoundBusy(info->soundInfo, params->sndChannel) && 
  355.             (info->faceSound != (Handle)nil)  &&
  356.              (!(Random()%100)) ) {
  357.             
  358.             PlaySound( info->soundInfo, params->sndChannel, info->faceSound);
  359.     }
  360.  
  361.  
  362.     // random chance to stop all this peering about and start sliding down
  363.     if (!(Random()%300) ) info->state = STATE_GOING_DOWN;
  364.     
  365. }
  366.  
  367.  
  368. void
  369. slide_picture_up(infostructPtr info, GMParamBlockPtr params)  {
  370.     Boolean *synchFlag;        /* pointer to speed up access to synch */
  371.     short DELTA;
  372.     GWorldPtr    worldPtr;        // temp ptr to help address stuff
  373.     GWorldPtr    currPort;        // something to save the port + device
  374.     GDHandle    currDev;
  375.             
  376.     // amount of pixels to slide is 1/5 the slider value ( {0..100} => {0..20} )
  377.     DELTA = 1 + params->controlValues[0] / 5;
  378.     
  379.     // if this is the first time, then setup theRect (where to draw on the screen)
  380.     if (info->theRect.top == 0 && info->theRect.left == 0) {
  381.         short leftrand;
  382.         // random horizontal offset on the screen so we don't always slide in 
  383.         // up from the same place
  384.         leftrand = RangedRdm( 0, (info->theScreen.right - info->theScreen.left)/2);
  385.         OffsetRect( &(info->theRect), 
  386.                 (info->theScreen).left + leftrand,  (info->theScreen).bottom);
  387.     }
  388.     
  389.     // slide them up a DELTA amount
  390.     OffsetRect( &(info->theRect), 0, -DELTA);
  391.     
  392.     // if they've bumped the top, or moved up so far that their bottom is above
  393.     // the screen's bottom, 
  394.     // or if they've moved so the eyes show and random chance
  395.     // it's time to stop moving up all together
  396.     
  397.     if ( (info->theRect.top <= info->theScreen.top) ||
  398.             (info->theRect.bottom <= info->theScreen.bottom) ||
  399.                 // if we're up past where the eyes show ... then randomly stop here
  400.                 (((info->theRect.top + info->eyesPt.v +
  401.                     ((*info->eyes)->picFrame.bottom - (*info->eyes)->picFrame.top ))
  402.                     < info->theScreen.bottom) && 
  403.                     (info->eyesPt.v != -1) &&
  404.                     !(Random()%20))
  405.             ) 
  406.     {
  407.         OffsetRect( &(info->theRect), 0, DELTA);    // move it back down a bit
  408.         info->state = STATE_UP;                        // we're all the way up now
  409.     } else {
  410.         PixMapHandle    pixBase, screenBase;
  411.  
  412.         worldPtr = info->gMyOffG;
  413.         GetGWorld(&currPort,&currDev);
  414.         
  415.         ForeColor (blackColor);
  416.         BackColor (whiteColor);
  417.         
  418.         pixBase = GetGWorldPixMap( worldPtr );
  419.         LockPixels ( pixBase );
  420.         
  421.         // wait for vertical retrace
  422.         SynchVBL(0);
  423.         // blit it into place onscreen
  424.         CopyBits ( (BitMap *) (*pixBase),
  425.                     &((GrafPtr)currPort)->portBits, 
  426.                                                                     //&info->theRect, 
  427.                     &(worldPtr->portRect),
  428.                     &info->theRect, 
  429.                     srcCopy, nil);
  430.         UnlockPixels(pixBase);
  431.     }
  432. }
  433.  
  434. void
  435. slide_picture_down(infostructPtr info, RgnHandle blankRgn, GMParamBlockPtr params)  {
  436.     Boolean *synchFlag;        /* pointer to speed up access to synch */
  437.     Rect diffRect;
  438.     short DELTA;
  439.     GWorldPtr    worldPtr;        // temp ptr to help address stuff
  440.     GWorldPtr    currPort;        // something to save the port + device
  441.     GDHandle    currDev;
  442.     
  443.     DELTA = 1 + params->controlValues[0] / 5;
  444.     
  445.     OffsetRect( &(info->theRect), 0, DELTA);        // slide downward
  446.     
  447.     // if we've slid so far down that our top leaves the screen, we're done going down
  448.     if ( info->theRect.top >= info->theScreen.bottom) {
  449.         info->state = STATE_DOWN;    
  450.         // I'm anal retentive, so let's just make sure the screen if cleared...
  451.         FillRgn(blankRgn, params->qdGlobalsCopy->qdBlack);
  452.         // set the rect to 0,0 offset so we recognize the initial condition in slide_up
  453.         SetRect( &(info->theRect), 0, 0, info->thePictWidth, info->thePictHeight);
  454.     } else {
  455.         worldPtr = info->gMyOffG;
  456.         GetGWorld(&currPort,&currDev);
  457.     
  458.         ForeColor (blackColor);
  459.         BackColor (whiteColor);
  460.         
  461.         SynchVBL(0);
  462.  
  463.         CopyBits ( (BitMap *) (*(worldPtr->portPixMap)),
  464.                     &((GrafPtr)currPort)->portBits, 
  465.                     &(worldPtr->portRect),
  466.                     &info->theRect, 
  467.                     srcCopy, nil);
  468.     }
  469. }
  470.  
  471.  
  472. // face is offscreen ... so it knocks on the glass a bit.
  473. void
  474. do_some_knocking(infostructPtr temp, GMParamBlockPtr params) {
  475.  
  476.     // if it is time to make noise again ...
  477.     if (TickCount() >= temp->nextSoundTick) {
  478.         // if it's not busy and we have sound and we have a sound resource, then do it
  479.         if (!SoundBusy(temp->soundInfo, params->sndChannel) && 
  480.                 (temp->oneKnockSound != (Handle)nil) )
  481.         {
  482.             short i, knocks;
  483.             long tempticks;
  484.             
  485.             // random number of knocks
  486.             knocks = RangedRdm(2, 6);
  487.             for (i=0; i<=knocks; i++) {
  488.                 PlaySound( temp->soundInfo, params->sndChannel, temp->oneKnockSound);
  489.                 // delay a random amount to give it some "human" like feel
  490.                 Delay(     RangedRdm(7, 12), &tempticks);
  491.                 
  492.             }
  493.             // lets not make sound until some amount later {5..15} seconds
  494.             temp->nextSoundTick = TickCount() + RangedRdm(300, 900);
  495.         }
  496.     } 
  497.     
  498.     // random chance to just stop all this knocking and start the show
  499.     if (!(Random()%200)) {
  500.         temp->nextSoundTick = 0;
  501.         temp->state = STATE_GOING_UP;
  502.     }
  503. }
  504.  
  505.  
  506.  
  507. OSErr DoSelected(RgnHandle blankRgn, short message, GMParamBlockPtr params)
  508. {
  509.     // I tried playing with params here and they don't seem instantiated,
  510.     // so don't play too much in this routine
  511.     return noErr;
  512. }
  513.  
  514.  
  515. // this is from the Think C reference code example ...
  516. unsigned short RangedRdm( unsigned short min, unsigned short max )
  517. /* assume that min is less than max */
  518. {
  519.     unsigned    qdRdm;    /* treat return value as 0-65536 */
  520.     long    range, t;
  521.     
  522.     // just to be safe, I'll put this here
  523.     if (min > max) DebugStr("\pMin greater then Max in RangedRdm");
  524.     
  525.     qdRdm = Random();
  526.     range = max - min;
  527.     t = (qdRdm * range) / 65536;     /* now 0 <= t <= range */
  528.     return( t+min );
  529. }
  530.  
  531.  
  532.  
  533. OSErr DoAboutBox(RgnHandle blankRgn, short message, GMParamBlockPtr params)
  534. {
  535.     // I set the proper resource, but I never get this call!!!???
  536.     SysBeep(0);        // lets see if we ever get this verdammnt routine called.
  537.     return noErr;
  538. }
  539.